/*
 *
 *  * Copyright (c) 2016. For Intelligent Group.
 *
 */

package com.intelligent.ispc.client.chart;


import com.google.common.collect.Lists;
import com.intelligent.ispc.chart.spc.ControlChart;
import com.intelligent.ispc.client.core.modal.spc.ChartDataModal;
import com.intelligent.ispc.client.ex.DoubleJSlider;
import com.intelligent.ispc.client.utils.GBC;
import com.intelligent.ispc.common.utils.MathUtils;
import com.intelligent.ispc.common.utils.StringUtils;
import com.intelligent.ispc.core.dto.ChartDto;
import com.intelligent.ispc.foundation.chart.util.ChartColor;
import com.intelligent.ispc.r.dto.ControlDataDto;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.labels.StandardCrosshairLabelGenerator;
import org.jfree.chart.panel.CrosshairOverlay;
import org.jfree.chart.plot.Crosshair;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.math.BigDecimal;
import java.text.DecimalFormat;

/**
 * Created by cherry on 2016/8/6.
 * Modified by QiangChen 2016/11/7
 */
public class ControlChartDynamicLineChart extends ControlChart {
    private JPanel panel;
    private ChartPanel chartPanel;
    private CrosshairOverlay localCrosshairOverlay;
    private SliderEx sliderEx1;
    private SliderEx sliderEx2;
    private String digitNumFormat;
    private int digitNum;

    /**
     * Constructor.
     *
     * @param controlDataDto line data.
     * @param chartDto       chart data.
     */
    public ControlChartDynamicLineChart(ControlDataDto controlDataDto, ChartDto chartDto) {
        super(controlDataDto, chartDto);
        panel = new JPanel();
        panel.setLayout(new GridBagLayout());
    }

    /**
     * Assemble the ChartDto used to draw the slider line
     *
     * @return return chartDto used to draw the slider line
     */
    public ChartDto exportBuildChart() {
        ChartDto chartDto = new ChartDto();
        chartDto.setName(ChartDataModal.CHART_RUN);
        java.util.List<Double> xline = Lists.newArrayList();
        double value1 = sliderEx1.getCrosshair().getValue();
        double value2 = sliderEx2.getCrosshair().getValue();
        xline.add(value1);
        xline.add(value2);
        chartDto.setXline(xline);
        return chartDto;
    }

    /**
     * Return the panel of slider.
     *
     * @return panel.
     */
    public JPanel getPanel() {
        double min = super.getMinY() < MathUtils.findMin(super.getSourceData().getMeanData()) ? super.getMinY() : MathUtils.findMin(super.getSourceData().getMeanData());
        double max = super.getMaxY() < MathUtils.findMax(super.getSourceData().getMeanData()) ? MathUtils.findMax(super.getSourceData().getMeanData()) : super.getMaxY();
        setDigitNumFormat(this.getDigNumFormat());
        sliderEx1 = new SliderEx(min, max, min, this.getDigitNum());
        sliderEx1.setCrossHairColor(ChartColor.NAVY);
        sliderEx2 = new SliderEx(min, max, min, this.getDigitNum());
        sliderEx2.setCrossHairColor(ChartColor.NAVY);
        chartPanel = super.createChartPanel();
        chartPanel.setLayout(new GridBagLayout());
        Crosshair crosshair1 = sliderEx1.getCrosshair();
        Crosshair crosshair2 = sliderEx2.getCrosshair();
        localCrosshairOverlay = new CrosshairOverlay();
        localCrosshairOverlay.addRangeCrosshair(crosshair1);
        localCrosshairOverlay.addRangeCrosshair(crosshair2);
        chartPanel.addOverlay(localCrosshairOverlay);
        sliderEx1.addSliderChangeListener();
        sliderEx1.addCheckboxChangeListener();
        sliderEx1.addTextFieldInsertListener();
        sliderEx2.addSliderChangeListener();
        sliderEx2.addCheckboxChangeListener();
        sliderEx2.addTextFieldInsertListener();

        panel.add(chartPanel, new GBC(0, 0).setFill(GBC.BOTH).setWeight(100, 100));
        panel.add(sliderEx1, new GBC(1, 0).setFill(GBC.BOTH).setWeight(5, 100));
        panel.add(sliderEx2, new GBC(2, 0).setFill(GBC.BOTH).setWeight(5, 100).setInsets(0, 3, 0, 0));
        return panel;
    }

    /**
     * Slider component.
     */
    private class SliderEx extends JPanel {
        private DoubleJSlider slider;
        private JCheckBox checkBox;
        private JTextField textField;
        private Crosshair crosshair;
        private double minVal;
        private double maxVal;
        private double remainder;
        private static final int FRACTIONAL_DIGITS = 12;

        SliderEx() {
            slider = new DoubleJSlider();
            initComponents();
        }

        SliderEx(double min, double max, double value, int digit) {
            final double factor = 0.1;
            final int lowerBound = 0;
            final int upperBound = 100;
            double minValue = min - (max - min) * factor;
            double maxValue = max + (max - min) * factor;
            this.minVal = minValue;
            this.maxVal = maxValue;
            final double initValue = (value - minValue) / (maxValue - minValue) * 100;
            slider = new DoubleJSlider(lowerBound, upperBound, initValue, digit);
            initComponents();
        }

        public void setCrossHairColor(Color color) {
            crosshair.setPaint(color);
        }

        public void setCrossHairLabelVisible(boolean bool) {
            crosshair.setLabelVisible(bool);
        }

        public void setCrossHairLabelBackColor(Color color) {
            crosshair.setLabelBackgroundPaint(color);
        }

        private void initComponents() {
            checkBox = new JCheckBox();
            textField = new JTextField(6);
            checkBox.setSelected(true);
            crosshair = new Crosshair(0.0D);

            crosshair.setLabelVisible(false);
            crosshair.setLabelBackgroundPaint(ChartColor.YELLOW);
            slider.setOrientation(SwingConstants.VERTICAL);
            this.setLayout(new GridBagLayout());
            this.add(checkBox, new GBC(0, 0).setFill(GBC.CENTER).setWeight(100, 100));
            this.add(slider, new GBC(0, 1).setFill(GBC.BOTH).setWeight(100, 100));
            this.add(textField, new GBC(0, 2).setFill(GBC.HORIZONTAL).setWeight(100, 100));
        }

        /**
         * Listen to the slider, change the value of the slider and promptly update the value in the textField.
         */
        public void addSliderChangeListener() {
            slider.addChangeListener(new ChangeListener() {
                @Override
                public void stateChanged(ChangeEvent e) {
                    crosshair.setValue(new BigDecimal(getSliderValue()).setScale(getDigitNum(), BigDecimal.ROUND_HALF_UP).doubleValue());
                    crosshair.setLabelGenerator(new StandardCrosshairLabelGenerator("" + crosshair.getValue(), new DecimalFormat(getDigitNumFormat())));
                    crosshair.setLabelVisible(true);
                    if (StringUtils.isBlank(textField.getText()) || !StringUtils.isNumeric(textField.getText())) {
                        EventQueue.invokeLater(new Runnable() {
                            public void run() {
                                textField.setText(new DecimalFormat(getDigitNumFormat()).format(getSliderValue()));
                                Thread.currentThread().interrupt();
                            }
                        });
                    } else if (new BigDecimal(getSliderValue()).setScale(getDigitNum(), BigDecimal.ROUND_HALF_UP).doubleValue() != Double.valueOf(textField.getText())) {
                        EventQueue.invokeLater(new Runnable() {
                            public void run() {
                                textField.setText(new DecimalFormat(getDigitNumFormat()).format(getSliderValue()));
                                Thread.currentThread().interrupt();
                            }
                        });
                    }
                }
            });
        }

        public void addCheckboxChangeListener() {
            checkBox.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    toggleCheckbox();
                    if (getCheckBox().isSelected()) {
                        localCrosshairOverlay.addRangeCrosshair(crosshair);
                    } else {
                        localCrosshairOverlay.removeRangeCrosshair(crosshair);
                    }
                }
            });
        }

        public void addTextFieldInsertListener() {
            textField.getDocument().addDocumentListener(new DocumentListener() {
                @Override
                public void insertUpdate(DocumentEvent e) {
                    try {
                        textChangeMethod(e);
                    } catch (BadLocationException e1) {
                        e1.printStackTrace();
                    }
                }

                @Override
                public void removeUpdate(DocumentEvent e) {
                    try {
                        textChangeMethod(e);
                    } catch (BadLocationException e1) {
                        e1.printStackTrace();
                    }
                }

                @Override
                public void changedUpdate(DocumentEvent e) {
                    try {
                        textChangeMethod(e);
                    } catch (BadLocationException e1) {
                        e1.printStackTrace();
                    }
                }
            });
        }

        public double getSliderValue() {
            final double sliderValue = new BigDecimal(this.slider.getDoubleValue() + getRemainder()).setScale(FRACTIONAL_DIGITS, BigDecimal.ROUND_HALF_UP).doubleValue() / 100.0 * (maxVal - minVal) + minVal;
            return sliderValue;
        }

        public void toggleCheckbox() {
            if (checkBox.isSelected()) {
                slider.setEnabled(true);
                textField.setEnabled(true);
            } else {
                slider.setEnabled(false);
                textField.setEnabled(false);
            }
        }

        /*
        * When the value in the textField changes and the value is valid, the value in the slider is updated in time.
        * */
        private void textChangeMethod(DocumentEvent e) throws BadLocationException {
            String content = e.getDocument().getText(0, textField.getText().length());
            if (StringUtils.isNumeric(content) && Double.valueOf(content) <= getMaxVal() && Double.valueOf(content) >= getMinVal()) {
                double textValue = Double.valueOf(content);
                final double sliderValue = (textValue - getMinVal()) / (getMaxVal() - getMinVal()) * 100.0;
                setRemainder(new BigDecimal(sliderValue - (int) (sliderValue)).setScale(FRACTIONAL_DIGITS, BigDecimal.ROUND_HALF_UP).doubleValue());
                slider.setDoubleValue(sliderValue);
                crosshair.setValue(new BigDecimal(textValue).setScale(getDigitNum(), BigDecimal.ROUND_HALF_UP).doubleValue());
                crosshair.setLabelGenerator(new StandardCrosshairLabelGenerator("" + crosshair.getValue(), new DecimalFormat(getDigitNumFormat())));
                crosshair.setLabelVisible(true);
            } else {
                crosshair.setLabelVisible(false);
                crosshair.setValue(0);
            }
        }

        public void setSliderRange(int min, int max, int value) {
            slider.setMinimum(min);
            slider.setMaximum(max);
            slider.setValue(value);
        }

        public DoubleJSlider getSlider() {
            return slider;
        }

        public void setSlider(DoubleJSlider slider) {
            this.slider = slider;
        }

        public JCheckBox getCheckBox() {
            return checkBox;
        }

        public void setCheckBox(JCheckBox checkBox) {
            this.checkBox = checkBox;
        }

        public JTextField getTextField() {
            return textField;
        }

        public void setTextField(JTextField textField) {
            this.textField = textField;
        }

        public Crosshair getCrosshair() {
            return crosshair;
        }

        public void setCrosshair(Crosshair crosshair) {
            this.crosshair = crosshair;
        }

        public double getMaxVal() {
            return maxVal;
        }

        public void setMaxVal(double maxVal) {
            this.maxVal = maxVal;
        }

        public double getMinVal() {
            return minVal;
        }

        public void setMinVal(double minVal) {
            this.minVal = minVal;
        }

        public double getRemainder() {
            return remainder;
        }

        public void setRemainder(double remainder) {
            this.remainder = remainder;
        }
    }

    public ChartPanel getChartPanel() {
        return chartPanel;
    }

    public void setChartPanel(ChartPanel chartPanel) {
        this.chartPanel = chartPanel;
    }

    public SliderEx getSliderEx1() {
        return sliderEx1;
    }

    public void setSliderEx1(SliderEx sliderEx1) {
        this.sliderEx1 = sliderEx1;
    }

    public SliderEx getSliderEx2() {
        return sliderEx2;
    }

    public void setSliderEx2(SliderEx sliderEx2) {
        this.sliderEx2 = sliderEx2;
    }

    public String getDigitNumFormat() {
        return digitNumFormat;
    }

    public void setDigitNumFormat(String digitNumFormat) {
        this.digitNumFormat = digitNumFormat;
    }

    public int getDigitNum() {
        return digitNum;
    }

    public void setDigitNum(int digitNum) {
        this.digitNum = digitNum;
    }

}

